Skip to content

feat(checkpoint): wire checkpointing into agent event loop - #2190

Merged
JackYPCOnline merged 1 commit into
strands-agents:mainfrom
JackYPCOnline:checkpoint_1
Jun 3, 2026
Merged

feat(checkpoint): wire checkpointing into agent event loop#2190
JackYPCOnline merged 1 commit into
strands-agents:mainfrom
JackYPCOnline:checkpoint_1

Conversation

@JackYPCOnline

@JackYPCOnline JackYPCOnline commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Description

Wires the Checkpoint data model (landed in #2181) into the agent runtime so an opt-in checkpointing=True agent pauses at ReAct cycle boundaries and emits a serializable pause-point marker. The marker travels through AgentResult.checkpoint and is passed back to a fresh agent through a checkpointResume block. The design mirrors the existing interrupt pattern: stop_reason="checkpoint" to signal the pause, content-block resume, no new method surface to learn.

A Checkpoint is a position marker (which boundary fired and which cycle index), not a state snapshot. State persistence is the caller's responsibility. The recommended pairing is a SessionManager for state continuity plus checkpointing=True for boundary signalling.

User-facing API (zero breaking changes — opt-in only):

from strands import Agent
from strands.session import FileSessionManager

agent = Agent(
    tools=[...],
    session_manager=FileSessionManager(session_id="run-1", storage_dir="..."),
    checkpointing=True,
)

result = await agent.invoke_async("do the thing")

while result.stop_reason == "checkpoint":
    save_somewhere(result.checkpoint.to_dict())
    # later, possibly in a fresh process / activity:
    fresh = Agent(
        tools=[...],
        session_manager=FileSessionManager(session_id="run-1", storage_dir="..."),
        checkpointing=True,
    )
    result = await fresh.invoke_async(
        {"checkpointResume": {"checkpoint": load_somewhere()}}
    )

print(result.message)  # stop_reason == "end_turn"

V0 known limitations:

  • Metrics reset on each resume call.
  • OpenAIResponsesModel(stateful=True) not supported.
  • BeforeInvocationEvent / AfterInvocationEvent fire on every resume (same as interrupts).
  • Per-tool granularity within a cycle requires a custom ToolExecutor. The SDK checkpoint operates at cycle boundaries.
  • Streaming callbacks do not re-emit on replay.

Related Issues

Documentation PR

Type of Change

New feature

Testing

Verified the changes do not break functionality or introduce warnings in consuming repositories.

  • I ran hatch run prepare

Evidence from fresh runs:

  • hatch test — 3026 passed, 4 skipped, 0 failed.
  • hatch run hatch-static-analysis:lint-checkruff check and mypy both clean.
  • hatch run hatch-static-analysis:format-check — all files formatted.\

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly (user-guide page is a follow-up PR in agent-docs; module-level docstring in checkpoint.py covers V0 limitations, precedence rules, and the recommended SessionManager pairing)
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed (reference Temporal / Dapr / Step Functions examples are the next milestone in the durable-execution tracking plan)
  • My changes generate no new warnings
  • Any dependent changes have been merged and published (Part A — feat: introduce checkpoint in experimental #2181 — is merged on main; this PR builds on it)

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@JackYPCOnline
JackYPCOnline marked this pull request as draft April 22, 2026 20:39
@codecov

codecov Bot commented Apr 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.62500% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
strands-py/src/strands/agent/agent.py 80.00% 3 Missing and 2 partials ⚠️
strands-py/src/strands/event_loop/event_loop.py 96.42% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Comment thread strands-py/src/strands/types/_events.py
Comment thread src/strands/event_loop/event_loop.py Outdated
Comment thread src/strands/agent/agent.py Outdated
Comment thread strands-py/src/strands/agent/agent.py
Comment thread src/strands/agent/agent.py Outdated
Comment thread strands-py/src/strands/event_loop/event_loop.py Outdated
Comment thread strands-py/src/strands/experimental/checkpoint/checkpoint.py
Comment thread tests/strands/experimental/checkpoint/test_checkpoint.py Outdated
Comment thread src/strands/agent/agent.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment

This is a well-structured PR that wires checkpoint functionality into the agent loop with a clean opt-in design. The state machine is carefully reasoned and the integration tests (especially the crash-after-tools test) are compelling. Two themes warrant attention before merge:

Review Themes
  • API Review Required: This introduces meaningful new public API surface (Agent parameter, AgentResult field, new StopReason, content block types). Per the API Bar Raising process, it needs a needs-api-review label and reviewer sign-off. Key design questions: is checkpointing: bool the right level of configurability, and should there be a high-level resume_from_checkpoint() method alongside the content-block primitive?

  • Error Contract Consistency: The resume validation comments claim to mirror _InterruptState.resume() conventions (TypeError/KeyError/ValueError), but Checkpoint.from_dict now raises CheckpointException. The exception hierarchy should be consistent and documented.

  • Coupling Pattern: The event loop directly accesses private agent attributes (_checkpointing, _checkpoint_resume_context). This mirrors the existing interrupt pattern but extends the coupling surface. Consider exposing checkpoint config as an explicit parameter or read-only property.

  • Test Coverage Gap: Missing a test for the checkpointing=True + end_turn (no tool use) path, and the Codecov report shows 1 partial line in event_loop.py.

The feature design, state-machine logic, and durability proof are solid. The integration tests are particularly well-designed.

Comment thread src/strands/event_loop/event_loop.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment

Good progress since the last round — the frozen=True dataclass, _build_checkpoint_stop_event extraction, and updated error convention documentation address several prior concerns. A few new items surfaced:

New Review Items
  • Docstring accuracy: event_loop_cycle Yields docstring still documents a 4-element tuple but the actual event is now 7 elements. The cancel() docstring uses "checkpoint" in a way that now conflicts with the durable-execution Checkpoint concept introduced here.
  • AGENTS.md update: The directory structure section needs to be updated to include experimental/checkpoint/ per the repo's own guidelines.
  • Cancel + checkpoint interaction: When both checkpointing=True and cancel_signal are set, checkpoint emission takes precedence over cancel. This is probably correct but should be documented or tested.

The prior-round items around API review (needs-api-review label, high-level resume method question) remain open for maintainer decision. The core state-machine logic and test coverage are solid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-persistence Session management or checkpointing enhancement New feature or request python Pull requests that update python code size/l

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants